using System;
using System.IO;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using Microsoft.Surface;
using Microsoft.Surface.Presentation;
using Microsoft.Surface.Presentation.Controls;

using Blip.API;
using Blip.API.Utility;
using Blip.JSONParser;
using Blip.UI.Controls;
using Blip.Tools;

namespace Blip.UI
{
    using MessageBox = Blip.UI.Controls.MessageBox;

    /// <summary>
    /// Główne okno aplikacji.
    /// </summary>
    public partial class MainWindow : SurfaceWindow
    {
        private IBlipFactory factory = new BlipFactory();

        private IBlipAPI api;

        private bool loggedIn = false;

        /// <summary>
        /// Konstruktor inicjalizujący główne okno aplikacji.
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();
            AddActivationHandlers();
            ApplicationLauncher.OrientationChanged += OnOrientationChanged;            
        }

        /// <summary>
        /// Przeprowadza operacje inicjalizacji komponentów.
        /// </summary>
        /// <param name="e">Argumenty zdarzenia inicjalizacji</param>
        protected override void OnInitialized(EventArgs e)
        {
            base.OnInitialized(e);
            api = factory.Create();
            sendBox.ButtonHit += new EventHandler<UpdateEventArgs>(AddStatus);
            loginForm.ButtonHit += new EventHandler<LoginEventArgs>(Login);
            loginForm.Cancel += delegate { HideLoginForm(); };
            ShowLoginForm();
        }

        private void OnOrientationChanged(object sender, OrientationChangedEventArgs e)
        {
            mainView.LayoutTransform = new RotateTransform(e.Orientation == UserOrientation.Top ? 180.0 : 0.0);
        }
        
        #region Initialization

        /// <summary>
        /// Przeprowadza dodatkowe operacje przy zamknięciu aplikacji.
        /// </summary>
        /// <param name="e">Argumenty zdarzenia</param>
        protected override void OnClosed(EventArgs e)
        {
            base.OnClosed(e);

            RemoveActivationHandlers();
        }

        private void AddActivationHandlers()
        {
            ApplicationLauncher.ApplicationActivated += OnApplicationActivated;
            ApplicationLauncher.ApplicationPreviewed += OnApplicationPreviewed;
            ApplicationLauncher.ApplicationDeactivated += OnApplicationDeactivated;
        }

        private void RemoveActivationHandlers()
        {
            ApplicationLauncher.ApplicationActivated -= OnApplicationActivated;
            ApplicationLauncher.ApplicationPreviewed -= OnApplicationPreviewed;
            ApplicationLauncher.ApplicationDeactivated -= OnApplicationDeactivated;
        }        

        private void OnApplicationActivated(object sender, EventArgs e)
        {

        }

        private void OnApplicationPreviewed(object sender, EventArgs e)
        {

        }

        private void OnApplicationDeactivated(object sender, EventArgs e)
        {

        }

        #endregion
        
        /// <summary>
        /// Zleca wykonanie operacji w wątku GUI.
        /// </summary>
        /// <param name="func">Procedura bezparametrowa przeznaczona do wykonania w wątku GUI</param>
        protected virtual void UIOperation(Action func)
        {
            mainView.Dispatcher.BeginInvoke(DispatcherPriority.Normal, func);
        }

        /// <summary>
        /// Zleca wykonanie operacji w wątku GUI.
        /// </summary>
        /// <param name="func">Procedura przeznaczona do wykonania w wątku GUI</param>
        /// <param name="arg">Parametr przekazany do metody [func]</param>
        protected virtual void UIOperation(Delegate func, object arg)
        {
            mainView.Dispatcher.BeginInvoke(DispatcherPriority.Normal, func, arg);
        }               

        private void Login(object sender, LoginEventArgs e)
        {
            clock.Show();
            loginForm.Freeze();            
            IBlipAPI newAPI = factory.Create(e.Login, e.Password);
            newAPI.GetProfile(response =>
                UIOperation(delegate
                {
                    clock.Hide();
                    loginForm.Enable();
                    if (response is BlipError)
                    {
                        messageBox.Show(MessageBox.ERROR, "Logowanie", "Próba logowania nie powiodła się. Nieprawidłowa nazwa użytkownika lub hasło.");
                    }
                    else
                    {
                        api = newAPI;
                        loggedIn = true;
                        messageBox.Show(MessageBox.OK, "Logowanie", "Logowanie zakończone sukcesem.");
                        SwitchMenu();
                        ShowSendBox();
                        HideLoginForm();
                    }
                })
            );
        }

        private void Logout(object sender, RoutedEventArgs e)
        {
            api = factory.Create();
            loggedIn = false;
            SwitchMenu();
            ShowLoginForm();
            HideSendBox();
        }

        private void DisplayLoginForm(object sender, RoutedEventArgs e)
        {
            ShowLoginForm();
        } 

        private void AddStatus(object sender, UpdateEventArgs e)
        {
            clock.Show();
            sendBox.Freeze();
            api.AddStatus(response =>
                UIOperation(delegate
                {
                    clock.Hide();
                    sendBox.Enable();
                    sendBox.Clear();
                    if (response is BlipError)
                        messageBox.Show(MessageBox.ERROR, "Błąd", "Wystąpił błąd. Serwer zwrócił: " + response.Message);
                    else
                        messageBox.Show(MessageBox.OK, "Potwierdzenie", "Wiadomość została dodana.");
                })
            , e.Message);
        }

        private void GetAllStatuses(object sender, RoutedEventArgs e)
        {
            clock.Show();
            api.Include("user", "user[avatar]", "pictures");
            api.GetAllStatuses(response =>
                UIOperation(new BlipResponse(DisplayStripes), response)
            );
        }

        private void GetStatuses(object sender, RoutedEventArgs e)
        {
            clock.Show();
            api.Include("user", "user[avatar]", "pictures");
            api.GetStatuses(response =>
                UIOperation(new BlipResponse(DisplayStripes), response)
            );
        }

        private void GetUserStatuses(object sender, RoutedEventArgs e)
        {
            ScatterViewItem item = new ScatterViewItem();
            Input input = new Input();
            input.Prompt = "Wprowadź nazwę użytkownika:";
            item.Content = input;
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showInputDialog", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                Storyboard endStory = SetStoryTarget("hideInputDialog", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            input.Cancel += delegate { hideItem(); };

            input.ButtonHit += (sender2, e2) =>
            {
                hideItem();
                clock.Show();
                api.Include("user", "user[avatar]", "pictures");
                api.GetUserStatuses(response =>
                    UIOperation(new BlipResponse(DisplayStripes), response)
                , e2.Input);
            };
        }

        private void GetPrivateMessages(object sender, RoutedEventArgs e)
        {
            clock.Show();
            api.Include("user", "user[avatar]", "pictures");
            api.GetPrivateMessages(response =>
                UIOperation(new BlipResponse(DisplayStripes), response)
            );
        }

        private void GetTags(object sender, RoutedEventArgs e)
        {
            ScatterViewItem item = new ScatterViewItem();
            Input input = new Input();
            input.Prompt = "Wpisz tag wiadomości:";
            item.Content = input;
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showInputDialog", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                Storyboard endStory = SetStoryTarget("hideInputDialog", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            input.Cancel += delegate { hideItem(); };

            input.ButtonHit += (sender2, e2) =>
            {
                hideItem();
                clock.Show();
                api.Include("user", "user[avatar]", "pictures");
                api.GetTags(response =>
                    UIOperation(new BlipResponse(DisplayStripes), response)
                , e2.Input);
            };
        }

        private void GetBliposphere(object sender, RoutedEventArgs e)
        {
            clock.Show();
            api.Include("user", "user[avatar]", "pictures");
            api.GetBliposphere(response =>
                UIOperation(new BlipResponse(DisplayStripes), response)
            );
        }

        private void SetBackground(object sender, RoutedEventArgs e)
        {            
            ScatterViewItem item = new ScatterViewItem();
            item.Style = scatterView.FindResource("scatterGalleryStyle") as Style;
            Gallery gallery = new Gallery();
            gallery.Header = "Wybierz nową tapetę:";
            gallery.Collection = GetFiles("Resources/Wallpapers", "*.jpg");
            item.Content = gallery;
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showGallery", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                gallery.Freeze(); 
                Storyboard endStory = SetStoryTarget("hideGallery", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            gallery.Cancel += delegate { hideItem(); };

            gallery.Select += (sender2, e2) =>
            {
                UploadProgressBar bar = CreateProgressBar("Wysyłanie tapety");

                hideItem();
                api.SetBackground(response =>
                    UIOperation(delegate
                    {
                        if (response is BlipError)
                            messageBox.Show(MessageBox.ERROR, "Błąd", "Wystąpił błąd. \n" + response.Message);
                        else
                            messageBox.Show(MessageBox.OK, "Potwierdzenie", "Nowe tło zostało ustawione");
                    })
                , e2.Picture
                , progress => UIOperation(delegate
                {
                    bar.Progress = progress;
                }));
            };
        }

        private void SetAvatar(object sender, RoutedEventArgs e)
        {
            ScatterViewItem item = new ScatterViewItem();
            item.Style = scatterView.FindResource("scatterGalleryStyle") as Style;
            Gallery gallery = new Gallery();
            gallery.Header = "Wybierz nowy awatar:";
            gallery.Collection = GetFiles("Resources/Avatars", "*.jpg");
            item.Content = gallery;
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showGallery", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                gallery.Freeze();
                Storyboard endStory = SetStoryTarget("hideGallery", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            gallery.Cancel += delegate { hideItem(); };

            gallery.Select += (sender2, e2) =>
            {
                UploadProgressBar bar = CreateProgressBar("Wysyłanie awatara");

                hideItem();
                api.SetAvatar(response =>
                    UIOperation(delegate
                    {
                        if (response is BlipError)
                            messageBox.Show(MessageBox.ERROR, "Błąd", "Wystąpił błąd. \n" + response.Message);
                        else
                            messageBox.Show(MessageBox.OK, "Potwierdzenie", "Nowy awatar został ustawiony");
                    })
                , e2.Picture
                , progress => UIOperation(delegate
                {
                    bar.Progress = progress;
                }));
            };
        }        

        private void DelBackground(object sender, RoutedEventArgs e)
        {
            ScatterViewItem item = new ScatterViewItem();
            Dialog dialog = new Dialog();
            item.Content = dialog;
            dialog.Question = "Czy na pewno chcesz skasować tło?";
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showDialog", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                dialog.Freeze();
                Storyboard endStory = SetStoryTarget("hideDialog", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            dialog.No += delegate { hideItem(); };

            dialog.Yes += delegate
            {
                hideItem();
                clock.Show();
                api.DelBackground(response =>
                    UIOperation(delegate
                    {
                        clock.Hide();
                        if (response is BlipError)
                            messageBox.Show(MessageBox.ERROR, "Błąd", "Wystąpił błąd. \n" + response.Message);
                        else
                            messageBox.Show(MessageBox.OK, "Potwierdzenie", "Tło zostało skasowane");
                    })
                );
            };
        }

        private void DelAvatar(object sender, RoutedEventArgs e)
        {
            ScatterViewItem item = new ScatterViewItem();
            Dialog dialog = new Dialog();
            item.Content = dialog;
            dialog.Question = "Czy na pewno chcesz skasować awatar?";
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showDialog", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                dialog.Freeze();
                Storyboard endStory = SetStoryTarget("hideDialog", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            dialog.No += delegate { hideItem(); };

            dialog.Yes += delegate            
            {
                hideItem();
                clock.Show();
                api.DelAvatar(response =>
                    UIOperation(delegate
                    {
                        clock.Hide();
                        if (response is BlipError)
                            messageBox.Show(MessageBox.ERROR, "Błąd", "Wystąpił błąd. \n" + response.Message);
                        else
                            messageBox.Show(MessageBox.OK, "Potwierdzenie", "Twój awatar został skasowany");
                    })
                );
            };
        }

        private void DisplayStripes(dynamic response)
        {
            clock.Hide();

            if (response is BlipError)
            {
                messageBox.Show(MessageBox.ERROR, "Błąd", "Wystąpił błąd. Serwer zwrócił\n" + response.Message);
                return;
            }

            for (int i = 0; i < response.Length; ++i)
            {
                try
                {
                    StatusStrip stripe = new StatusStrip();
                    stripe.Status = response[i]["body"];
                    stripe.Info = response[i]["created_at"];
                    switch (response[i]["type"] as string)
                    {
                        case "Status":
                        case "DirectedMessage":
                            stripe.Type = StatusStrip.TYPE.UPDATE;
                        break;
                        case "PrivateMessage":
                            stripe.Type = StatusStrip.TYPE.PRIVATE_MESSAGE;
                        break;
                    }

                    if (response[i]["user"].ContainsKey("avatar") && response[i]["user"]["avatar"].ContainsKey("url_90"))
                    {
                        stripe.Avatar = new BitmapImage(new Uri("http://blip.pl" + response[i]["user"]["avatar"]["url_90"]));
                    }

                    if (response[i].ContainsKey("pictures") && response[i]["pictures"].Length > 0)
                    {
                        var picture = response[i]["pictures"][0];
                        BitmapImage bitmap = new BitmapImage(new Uri(picture["url"]));
                        stripe.Picture = new BitmapImage(new Uri(picture["url"]));
                    }

                    AddStripe(stripe);
                }
                catch { }
            }
        }

        /// <summary>
        /// Dodaje nowy pasek statusu do kontrolki ScatterView.
        /// </summary>
        /// <param name="stripe">Obiekt statusu</param>
        protected void AddStripe(StatusStrip stripe)
        {
            ScatterViewItem item = new ScatterViewItem();
            item.Style = scatterView.FindResource("scatterStripeStyle") as Style;
            item.Content = stripe;
            stripe.Close += delegate
            {
                Storyboard story = SetStoryTarget("hideStripe", item);
                story.Begin(this);
                story.Completed += delegate { scatterView.Items.Remove(item); };
            };
            scatterView.Items.Add(item);
        }

        private void ClearUpdates(object sender, RoutedEventArgs e)
        {
            ClearStripes(StatusStrip.TYPE.UPDATE);
        }

        private void ClearPrivateMessages(object sender, RoutedEventArgs e)
        {
            ClearStripes(StatusStrip.TYPE.PRIVATE_MESSAGE);
        }

        private void ClearStripes(StatusStrip.TYPE type)
        {
            ScatterViewItem item = new ScatterViewItem();
            Dialog dialog = new Dialog();
            item.Content = dialog;
            dialog.Question = "Czy na pewno chcesz wyczyścić statusy?";
            scatterView.Items.Add(item);
            Storyboard story = SetStoryTarget("showDialog", item);
            story.Begin(this);

            Action hideItem = delegate
            {
                dialog.Freeze();
                Storyboard endStory = SetStoryTarget("hideDialog", item);
                endStory.Completed += delegate { scatterView.Items.Remove(item); };
                endStory.Begin(this);
            };

            dialog.No += delegate { hideItem(); };

            dialog.Yes += delegate
            {
                hideItem();
                var children = new List<ScatterViewItem>();
                foreach (ScatterViewItem child in scatterView.Items)
                    if (child.Content is StatusStrip && (child.Content as StatusStrip).Type == type)
                        children.Add(child);

                foreach (ScatterViewItem child in children)
                    scatterView.Items.Remove(child);
            };
        }

        private void About(object sender, RoutedEventArgs e)
        {
            messageBox.Show(MessageBox.INFO, "O programie", "Blip Surface (c) 2010\nPiotr\t\tŁukasz\nSkotnicki\tHolek");
        }

        /// <summary>
        /// Wczytuje listę plików o określonym wzorcu, z podanej lokalizacji.
        /// </summary>
        /// <param name="path">Ścieżka katalogu</param>
        /// <param name="pattern">Wzór plików</param>
        /// <returns>Lista dopasowanych plików</returns>
        protected IEnumerable<string> GetFiles(string path, string pattern = "*.*")
        {
            string dirPath = AppDomain.CurrentDomain.BaseDirectory + path;
            string[] files = Directory.GetFiles(dirPath, pattern);
            return files;
        }

        /// <summary>
        /// Tworzy i zwraca nowy pasek postępu. Pasek zostaje umieszczony w panelu ProgressBarPanel.
        /// </summary>
        /// <param name="label">Etykieta paska postępu</param>
        /// <returns>Obiekt paska postępu</returns>
        protected UploadProgressBar CreateProgressBar(string label)
        {
            UploadProgressBar bar = new UploadProgressBar();
            bar.Label = label;
            uploadProgressPanel.AddBar(bar);
            return bar;
        }

        /// <summary>
        /// Przełącza menu w zależności od tego czy użytkownik jest zalogowany, czy nie.
        /// </summary>
        protected void SwitchMenu()
        {
            if (loggedIn)
            {
                mainMenuLoggedIn.Visibility = Visibility.Visible;
                mainMenuLoggedOut.Visibility = Visibility.Hidden;
            }
            else
            {
                mainMenuLoggedOut.Visibility = Visibility.Visible;
                mainMenuLoggedIn.Visibility = Visibility.Hidden;
            }
        }

        /// <summary>
        /// Wyświetla kontrolkę SendBox.
        /// </summary>
        protected void ShowSendBox()
        {
            if (siSendBox.Visibility == Visibility.Hidden)
            {
                siSendBox.Visibility = Visibility.Visible;
                sendBox.Clear();
                PlayStory("showSendBox");
            }
        }

        /// <summary>
        /// Ukrywa kontrolkę SendBox.
        /// </summary>
        protected void HideSendBox()
        {
            if (siSendBox.Visibility == Visibility.Visible)
                PlayStory("hideSendBox");
        }

        private void HideSendBoxCompleted(object sender, EventArgs e)
        {
            siSendBox.Visibility = Visibility.Hidden;
        }

        /// <summary>
        /// Wyświetla formularz logowania.
        /// </summary>
        protected void ShowLoginForm()
        {
            if (siLoginForm.Visibility == Visibility.Hidden)
            {
                loginForm.Clear();
                loginForm.Enable();
                siLoginForm.Visibility = Visibility.Visible;
                PlayStory("showLoginForm");
            }
        }

        /// <summary>
        /// Ukrywa formularz logowania.
        /// </summary>
        protected void HideLoginForm()
        {
            if (siLoginForm.Visibility == Visibility.Visible)
            {
                loginForm.Freeze();
                PlayStory("hideLoginForm");
            }
        }

        private void HideLoginFormCompleted(object sender, EventArgs e)
        {
            siLoginForm.Visibility = Visibility.Hidden;
        }

        /// <summary>
        /// Wyszukuje i odtwarza wskazaną animację.
        /// </summary>
        /// <param name="key">Animacja do odtworzenia</param>
        protected void PlayStory(string key)
        {
            try
            {
                Storyboard story = FindResource(key) as Storyboard;
                story.Begin(this);
            }
            catch { }
        }

        /// <summary>
        /// Ustawia kontrolkę, na rzecz której odegrana zostanie animacja.
        /// </summary>
        /// <param name="storyName">Nazwa animacji</param>
        /// <param name="target">Obiekt animowany</param>
        /// <returns>Obiekt klasy animacji</returns>
        protected Storyboard SetStoryTarget(string storyName, DependencyObject target)
        {
            Storyboard story = FindResource(storyName) as Storyboard;
            foreach (var child in story.Children)
                Storyboard.SetTarget(child, target);
            return story;
        }
    }
}